package com.clp.protocol.modbus_tcp.client.master_state;

import com.clp.protocol.core.exception.EnumElemDoesNotExistException;
import com.clp.protocol.modbus_tcp.client.MasterImpl;
import com.clp.protocol.modbus_tcp.client.async.SendMasterPromise;
import com.clp.protocol.modbus_tcp.client.master_config.MasterConnConfig;
import com.clp.protocol.modbus_tcp.connect.state.AbstractState;
import com.clp.protocol.modbus_tcp.mb_frame.ReqMbFrm;
import com.clp.protocol.modbus_tcp.mb_frame.RespMbFrm;
import com.clp.protocol.modbus_tcp.mb_frame.mb_body.mb_data.RespOkMbData;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;

import java.util.Arrays;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 主站状态：所有的主站状态互不干扰
 *  在发送帧时，先更新主站状态，再更新连接状态。
 *  在接收帧时，先更新连接状态，再更新主站状态。
 */
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public abstract class MasterState extends AbstractState {

    public enum Type {
        SEND_RECV;

        public MasterState newMasterState(MasterImpl masterImpl, MasterConnConfig cfg) {
            switch (this) {
                case SEND_RECV:
                    return new SendRecvMasterState(masterImpl);
            }
            throw new EnumElemDoesNotExistException(Type.class);
        }
    }

    protected final MasterImpl masterImpl;

    public abstract Type type();

    /**
     * 因为写出了Apdu，所以要调整状态信息
     *
     * @param reqMbFrm
     */
    public abstract ReqMbFrm updateBySending(ReqMbFrm reqMbFrm);

    /**
     * 因为读入了Apdu，所以要调整状态信息
     *
     * @param respMbFrm
     */
    public abstract RespMbFrm updateByRecving(RespMbFrm respMbFrm);

    public abstract <V extends RespOkMbData> SendMasterPromise<V> register(SendMasterPromise<V> sendPromise);

    @Override
    protected ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) {
        return masterImpl.channel().eventLoop().scheduleAtFixedRate(task, initialDelay, period, unit);
    }

    public static ConcurrentMap<Type, MasterState> newMasterStateMap(MasterImpl masterImpl, MasterConnConfig cfg) {
        return Arrays.stream(Type.values())
                .collect(Collectors.toConcurrentMap(type -> type, type -> type.newMasterState(masterImpl, cfg)));
    }

}
